package queryOpt;

import java.util.ArrayList;

/**
 *
 * @author Administrator
 */
public class subPlan {

    private int num;  //number of terms in the query to be optimized.
    private int size; //number of terms in this subplan. For example, a subplan has the 1st, 3rd of all 4 terms, then the size will be 2.
    private int seq;  //10 base sequence number, corresponding to the 2 based bitmap.ranging from 1 to ((1<<this.num)-1).
    public int bitMap[];
    private ArrayList<Integer> list; //stores the sequence number of each term in this subset.
    private subPlan left;     //left child.
    private subPlan right;    //right child.
    private subPlan left_1;     //left child.
    private subPlan right_1;    //right child.
    private int branchOrNot;  //if branchOrNot is 0, this.bestcost stores the nobranch cost.
    private Double bestCost;
    private Double bestCost2;
    private Double noBranchCost;
    private Double logicalAndCost;
    private Double branchingAndCost;
    private Double cMetric;
    private Double dMetric;
    private Double p;     //result of multiplication of all selectivitis.
    private Double q;     //q in the algorithm. according to whether p>0.5 or not.
    private Double fCost;   //fcost in the algorithm.
    private int isRightest;

    public void setfcost(Double r, Double t, Double f, Double l) {
        this.fCost = this.size * r + (this.size - 1) * l + this.size * f + t;
    }

    public void subPlan(int seq, int n) {
        this.seq = seq;
        this.num = n;
        this.bestCost = 0.0;
        this.branchOrNot = 0;
        this.branchingAndCost = 0.0;
        this.logicalAndCost = 0.0;
        this.noBranchCost = 0.0;
        this.p = 1.0;
        this.q = 1.0;
        this.cMetric = 0.0;
        this.dMetric = 0.0;
        this.list = new ArrayList<Integer>();
        this.fCost = 0.0;
        this.isRightest = 0;
        this.bestCost2 = 0.0;
    }

    public int getSeq() {
        return this.seq;
    }

    public int getNum() {
        return this.num;
    }

    public int getisrightest() {
        return this.isRightest;
    }

    public Double getBestCost() {
        return this.bestCost;
    }

    public Double getP() {
        return this.p;
    }

    public Double getQ() {
        return this.q;
    }

    public Double getCMetric() {
        return this.cMetric;
    }

    public Double getDMetric() {
        return this.dMetric;
    }

    public Double getBranchingAndCost() {
        return this.branchingAndCost;
    }

    public Double getNoBranchCost() {
        return this.noBranchCost;
    }

    public Double getLogicAndCost() {
        return this.logicalAndCost;
    }

    public int getBranchSign() {
        return this.branchOrNot;
    }

    public Double getFCost() {
        return this.fCost;
    }

    public Double getBestCost2() {
        return this.bestCost2;
    }
    //Compares the nobranch cost and logic and cost, then set the best cost and nobranchcost sign.

    public void setInitBestCost() {
        if (this.logicalAndCost < this.noBranchCost) {
            this.bestCost = this.logicalAndCost;
            this.bestCost2 = this.logicalAndCost;
            this.branchOrNot = 0;
        } else {
            this.bestCost = this.noBranchCost;
            this.bestCost2 = this.logicalAndCost;
            this.branchOrNot = 1;
        }
    }

    public void setBestCost(Double temp) {
        this.bestCost = temp;
    }

    public void setBitMap() {   //set bitmap according to sequence number. 1-1 match.
        this.bitMap = new int[this.num];
        for (int i = 0; i < this.num; i++) {
            this.bitMap[i] = 0;
        }
        this.size = 0;
        for (int i = 0; i < this.num; i++) {
            //System.out.println(this.seq/(1<<i)%2);
            if ((this.seq / (1 << i) % 2) == 1) {
                this.bitMap[i] = 1;
                this.list.add(i + 1);
                this.size++;
            }
            //System.out.print(this.bitMap[i]);
        }
    }

    public int getOnes() {   //returns the number of 1's in the bitmap. or can be seen as the number of terms in this subplan.
        System.out.println("size:" + this.size);
        return this.size;
    }

    public int[] getBitMap() {
        return this.bitMap;
    }

    public void setLeftChild(subPlan left1) {
        this.left = left1;
    }

    public void setRightChild(subPlan right1) {
        this.right = right1;
    }

    public void setLeftChild_1(subPlan left1) {
        this.left_1 = left1;
    }

    public void setRightChild_1(subPlan right1) {
        this.right_1 = right1;
    }

    public void setPQ(ArrayList<Double> selectivity) {   //set this.p and this.q according to bitmap and selectivity info.
        this.p = 1.0;
        for (int i = 0; i < selectivity.size(); i++) {
            if (this.bitMap[i] == 1) {
                this.p = this.p * selectivity.get(i);
            }
        }
        if (this.p > 0.5) {
            this.q = 1 - this.p;
        } else {
            this.q = this.p;
        }
        System.out.println("sql:" + this.getSeq());
        System.out.println("p" + this.p);
    }

    public subPlan getLeftChild() {
        return this.left;
    }

    public subPlan getRightChild() {
        return this.right;
    }

    public subPlan getLeftChild_1() {
        return this.left_1;
    }

    public subPlan getRightChild_1() {
        return this.right_1;
    }

    public ArrayList<Integer> getList() {   //used to retrieve the sequence number of terms in this subset.
        return this.list;
    }

    public void setNoBranchCost(Double r, Double l, Double f, Double a) {
        this.noBranchCost = 0.0;
        int k = this.size;
        Double tempCost = 0.0;
        tempCost = this.size * r + (k - 1) * l + a;
        tempCost = tempCost + k * f;
        this.noBranchCost = tempCost;
    }

    public void setLogicalAndCost(Double r, Double l, Double f, Double t, Double m, ArrayList<Double> p, Double a) {
        int k = this.getOnes();
        Double tempCost = 0.0;

        tempCost = k * r + (k - 1) * l + t + m * this.q + a * this.p + f * k;

        this.logicalAndCost = tempCost;

    }

    public void setBestCost2(Double i) {

        this.bestCost2 = i;
    }

    public void setBranchingAndCost(Double r, Double l, Double f, Double t, Double m, ArrayList<Double> p, Double a) {
        Double cn = a;
        for (int i = this.size - 1; i >= 0; i--) {
            cn = r + t + f + m * this.q + cn * p.get(this.list.get(i) - 1);
        }
        this.branchingAndCost = cn;
    }

    public void setFCost(Double r, Double l, Double t, Double f) {
        Double tempFCost = 0.0;
        tempFCost = this.size * r + (this.size - 1) * l + t + this.size * f;

        this.fCost = tempFCost;
    }

    public Double getSelectivity(ArrayList<Double> selectivity) {
        Double temp = 1.0;
        for (int i = 0; i < this.size; i++) {
            temp = temp * selectivity.get(this.list.get(i));
        }
        return temp;
    }

    public void setCMetric(ArrayList<Double> selectivity) {
        //System.out.println("in cmetric set");
        this.cMetric = (this.getP() - 1.0);
        //System.out.println("in cmetric set1");
        this.setFCost(config.Get_r(), config.Get_l(), config.Get_t(), config.Get_f());
        //System.out.println("in cmetric set2");
        this.cMetric /= this.getFCost();
    }

    public void setDMetric(ArrayList<Double> selectivity) {
        this.dMetric = this.getFCost();
    }

    public void setBranchOrNot(int i) {
        this.branchOrNot = i;
    }

    public void initialization(Double r, Double l, Double f, Double t, Double m, ArrayList<Double> p, Double a) {
        //System.out.println("subset initialization.");
        this.setBitMap();
        //System.out.println("subset bitmap set.");
        this.setPQ(p);
        //System.out.println("subset p/q set.");
        this.setBranchingAndCost(r, l, f, t, m, p, a);
        //System.out.println("subset branch cost set.");
        this.setLogicalAndCost(r, l, f, t, m, p, a);
        //System.out.println("subset logic and cost.");
        this.setNoBranchCost(r, l, f, a);
        //System.out.println("subset no branch cost set.");
        setInitBestCost();
        setfcost(r, t, f, l);
        //System.out.println("Best cost set.");
        this.setCMetric(p);
        //System.out.println("subset c metric set.");
        this.setDMetric(p);
        //System.out.println("subset d metric set.");
        //  this.setFCost(r, l, t, f);
        //System.out.println("subset fcost set.");
        System.out.println(this.seq + ":branchcost" + this.logicalAndCost);
    }

    public int checkCommon(subPlan q) {  //checks whether subplan q has common term with this subplan.
        int flag = 1;
        for (int i = 0; i < this.num; i++) {
            if (this.bitMap[i] == q.bitMap[i] && this.bitMap[i] == 1) {
                flag = 0;
                break;
            }
        }
        return flag;
    }

    public int[] getReverseMap() {   //returns the reverse of bitmap. [1 0 0] -> [0 1 1]
        int temp[] = new int[this.num];
        for (int i = 0; i < this.num; i++) {
            temp[i] = 1 - this.bitMap[i];
        }
        return temp;
    }

    public int hasLeftChild() {
        if (this.getLeftChild() != null) {
            return 1;
        } else {
            return 0;
        }
    }

    public int hasRightChild() {
        if (this.getRightChild() != null) {
            return 1;
        } else {
            return 0;
        }
    }

    public void findRightest(boolean flag) {

        if (this.right == null) {
            if (flag) {
                this.isRightest = 1;
            }

            if (this.noBranchCost < this.logicalAndCost) {
                if (flag) {
                    this.bestCost2 = this.noBranchCost;
                    this.branchOrNot = 1;
                } else {
                    this.bestCost = this.noBranchCost;
                }

            } else {
                if (!flag) {
                    this.bestCost = this.branchingAndCost;
                }
            }
        } else {

            if (this.right.getNoBranchCost() < this.right.bestCost) {
                this.right.isRightest = 1;
                this.right.setLeftChild(null);
                this.right.setRightChild(null);
                this.bestCost2 = this.left.getFCost() + config.Get_m() * this.left.getQ() + this.getLeftChild().getP() * this.right.getNoBranchCost();
            } else {
                this.right.findRightest(flag);
            }




        }

    }

    public void showSelectivity(ArrayList<Double> selectivity) throws Exception {
        for (int i = 0; i < this.num; i++) {
            System.out.print(selectivity.get(i) + " ");
            FileOutput.write(selectivity.get(i) + " ");
        }
        System.out.print("\n");
        FileOutput.newline();
    }

    public void show() throws Exception {   //show plans of this subplan.

        if (this.getLeftChild() != null) {

            if (this.getRightChild().getisrightest() == 1 && this.getRightChild().getBranchSign() == 1) {
                this.getLeftChild().show();
                System.out.print(" \n");
                FileOutput.newline();
                System.out.print("{");
                FileOutput.write("{");

                this.getRightChild().show();
                System.out.print("}");
                FileOutput.write("}");
            } else {

                System.out.print("(");
                FileOutput.write("(");
                this.getLeftChild().show();
                System.out.print(" && ");
                FileOutput.write("&&");
                this.getRightChild().show();

            }
            //System.out.print(")");
            //System.out.print("\n");
        } else {

            if (this.isRightest == 0) {  //use branching and cost.
                System.out.print("(");
                FileOutput.write("(");
                for (int i = 0; i < this.list.size() - 1; i++) {
                    System.out.print("t" + this.list.get(i) + "[o" + this.list.get(i) + "[i]" + "] & ");
                    FileOutput.write("t" + this.list.get(i) + "[o" + this.list.get(i) + "[i]" + "] & ");
                }
                System.out.print("t" + this.list.get(this.list.size() - 1) + "[o" + this.list.get(this.list.size() - 1) + "[i]" + "])");
                FileOutput.write("t" + this.list.get(this.list.size() - 1) + "[o" + this.list.get(this.list.size() - 1) + "[i]" + "])");
                //System.out.print(this.list.get(this.list.size()-1));
            } else if (this.branchOrNot == 1) {  //use no branch cost.
                System.out.print("\n");
                FileOutput.newline();

                System.out.println("answer[j]=i;");
                FileOutput.write("answer[j]=i;");
                FileOutput.newline();
                System.out.print("j += (");
                FileOutput.write("j += (");
                for (int i = 0; i < this.list.size() - 1; i++) {
                    System.out.print("t" + this.list.get(i) + "[o" + this.list.get(i) + "[i]" + "] & ");
                    FileOutput.write("t" + this.list.get(i) + "[o" + this.list.get(i) + "[i]" + "] & ");
                }
                System.out.print("t" + this.list.get(this.list.size() - 1) + "[o" + this.list.get(this.list.size() - 1) + "[i]" + "]);");
                FileOutput.write("t" + this.list.get(this.list.size() - 1) + "[o" + this.list.get(this.list.size() - 1) + "[i]" + "]);");
                //System.out.print(")");
            } else {
                System.out.print("(");
                FileOutput.write("(");
                for (int i = 0; i < this.list.size() - 1; i++) {
                    System.out.print("t" + this.list.get(i) + "[o" + this.list.get(i) + "[i]" + "] & ");
                    FileOutput.write("t" + this.list.get(i) + "[o" + this.list.get(i) + "[i]" + "] & ");
                }
                System.out.print("t" + this.list.get(this.list.size() - 1) + "[o" + this.list.get(this.list.size() - 1) + "[i]" + "])");
                FileOutput.write("t" + this.list.get(this.list.size() - 1) + "[o" + this.list.get(this.list.size() - 1) + "[i]" + "])");
                System.out.print("){\nAnswer[j++] = i};");
                FileOutput.write(")");
                FileOutput.newline();
                FileOutput.write("{Answer[j++] = i};");
            }
            //System.out.print(")");
        }
    }
}
